#pragma once

#include <iostream>
#include <string>
#include <string.h>
#include <strings.h>
#include <fstream>
#include <sstream>
#include <vector>
#include <unordered_map>
#include <utility>

#include "Common.hpp"
#include "Log.hpp"

static const std::string sep = "\r\n";
static const std::string blankline = sep;
static const std::string flag = ": ";
static const std::string LineSep = " ";
static const std::string default_file_name = "wwwroot";
static const std::string http_version = "HTTP/1.1";
static const std::string page404 = "wwwroot/404.html";
static const std::string firstpage = "index.html";

// GET /favicon.ico HTTP/1.1\r\n
// Host: 8.137.19.140:8080
// Connection: keep-alive
// User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36 Edg/131.0.0.0
// Accept: image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8
// Referer: http://8.137.19.140:8080/?msg=i_have_sent_a_message_he_he_he_he_he_he_he_he_he_he_he_he_he_he_he_he_he_he_he_he_he_he_he_he_he_he_he_he_he_he_he_he_he_he_he_he_he_he_he_he_he_
// Accept-Encoding: gzip, deflate
// Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
// dnt: 1
// sec-gpc: 1
//

// 请求

using namespace LogArea;

class HttpRequest
{
public:
    HttpRequest()
    {
    }

    // 建立映射关系
    bool RequestKvHash()
    {
        for (auto &str : _req_segment)
        {
            std::string key, val;
            if (SplitRule(str, flag, &key, &val))
                _req_kv.insert(std::make_pair(key, val));
        }
        return true;
    }

    bool ParseHeader(std::string &req_str)
    {
        std::string str;
        while (true)
        {
            bool ret = ParseOneLine(req_str, &str, sep);
            if (ret && !str.empty()) // 还有消息且不为空行
                _req_segment.push_back(str);
            else if (ret && !str.empty()) // 还有消息但是读到了空行(也就是正文的前一行)
            {
                _blank_line = sep;
                break;
            }
            else
                return false;
        }

        // 建立映射关系
        RequestKvHash();
        return true;
    }
    // 获取请求消息,并进行反序列化
    bool Deserialization(std::string &req_str)
    {
        // 第一行获取成功了
        if (ParseOneLine(req_str, &_req_line, sep))
        {
            // 提取第一行细分消息, 分别为 _method _uri _path
            ParseReqLine();

            // 将后面正文以外的剩余报文存储
            ParseHeader(req_str);

            // 走到这理论上报文(req_str)中只剩下了正文
            _body = req_str;
        }
        return true;
    }

    // 获取网页信息: 获取文件内容    传入的是一个文件路径
    std::string GetContent(std::string path)
    {
        std::string content;
        std::ifstream in(path, std::ios::binary); // 以二进制形式打开文件，这样既可以读取图片也可以读取文本
        if (!in.is_open())
            return std::string();
        in.seekg(0, in.end);       // 设置文件当前指针位置
        int filesize = in.tellg(); // tellg 返回值为文件开始位置到文件当前指针位置的偏移量, 单位是字节
        in.seekg(0, in.beg);       // 设置文件当前指针位置

        content.resize(filesize);
        in.read((char *)content.c_str(), filesize); // 从文件指针位置开始读取 filesize 长度的数据, 存储在 content 中

        in.close();
        LOG(LogLevel::INFO) << "content length" << content.size();
        return content;  //返回文件中的内容，字符串
    }

    void Print()
    {
        std::cout << "_method: " << _method << std::endl;
        std::cout << "_uri: " << _uri << std::endl;
        std::cout << "_version: " << _version << std::endl;

        for (auto &it : _req_kv)
            std::cout << it.first << ": " << it.second << std::endl;

        std::cout << "_blank_line: " << _blank_line << std::endl;
        std::cout << "_body: " << _body << std::endl;
    }

    std::string GetUri()
    {
        return _uri;
    }

    void SetUri(std::string newuri)
    {
        _uri = newuri;
    }

    // 获取后缀
    std::string GetSuffix()
    {
        // _uri -> 1. wwwroot/index.html 2. wwwroot/image/1.jpg   3. wwwroot/login.html
        auto pos = _uri.rfind(".");
        if (pos == std::string::npos)
            return std::string(".html");
        return _uri.substr(pos);
    }

    std::string GetPath() { return _path; }

    ~HttpRequest()
    {
    }

private:
    bool ParseReqLine()
    {
        //_req_line以空格为分隔，使用stringstream正好可以解决
        std::stringstream ss(_req_line);
        ss >> _method >> _uri >> _path;

        return true;
    }

private:
    std::vector<std::string> _req_segment; // 存放每一行报文

    // Host: 8.137.19.140:8080   key:Host    Val:8.137.19.140:8080
    std::unordered_map<std::string, std::string> _req_kv; // 讲报文进行 kv 映射

    std::string _req_line;
    std::string _req_header; // 请求报头
    std::string _blank_line; // 空行
    std::string _body;       // 消息主体

    // 第一行内容细分出来的字段
    std::string _method;
    std::string _version; // 浏览器版本
    std::string _uri;     // 用户想要这个 // /login.html || /login?xxxxx
    std::string _path;    // 文件路径
};

class HttpResponse
{
public:
    HttpResponse() : _verion(http_version), _blank_line(sep)
    {
    }

    // 处理请求: 构建应答
    void Build(HttpRequest &request)
    {
        //构建文件路径+请求
        std::string uri = default_file_name + request.GetUri(); //"wwwroot" + "/a/b/c/d" == "wwwroot/a/b/c/d"

        // 如果没有请求或者结尾为 '/',设置默认访问主界面
        if (uri.back() == '/')
            uri += firstpage;
        LOG(LogLevel::DEBUG) << "客户端在请求: " << request.GetUri();
        request.Print();

        LOG(LogLevel::DEBUG) << "----------------------------";

        //读取文件内容
        _content = request.GetContent(uri);

        if (_content.empty())
        {
            // 用户请求的资源不存在, 将状态码设为 404
            _status_code = 404;
            _content = request.GetContent(page404);  //获取 404文件中的内容
        }
        else // 请求成功
            _status_code = 200;

        
        _status_desc = CodeToDesc(_status_code);
        if (!_content.empty())
        {
            SetHeader("Content length", std::to_string(_content.size()));
        }
        std::string mime_type = SuffixToDesc(request.GetSuffix());

        SetHeader("Content type", mime_type);
        _body = _content;
    }

    void SetHeader(const std::string &k, const std::string &v)
    {
        _header_kv[k] = v;
    }

    void SetCode(int code)
    {
        _status_code = code;
        _status_desc = CodeToDesc(_status_code);
    }

    void SetBody(const std::string &body)
    {
        _body = body;
    }

    //resp_str: 应答字符串
    void Serialize(std::string *resp_str)
    {
        for (auto &header : _header_kv)
        {
            _resp_header.push_back(header.first + flag + header.second);
        }
        _resp_line = _verion + LineSep + std::to_string(_status_code) + LineSep + _status_desc + sep;

        // 序列化
        *resp_str = _resp_line; // 第一行: 请求行
        // 请求报头
        for (auto &line : _resp_header)
        {
            *resp_str += (line + sep);
        }
        // 空行
        *resp_str += _blank_line;
        // 正文
        *resp_str += _body;
    }

    ~HttpResponse()
    {
    }

private:
    std::string CodeToDesc(const int &status_code)
    {
        switch (status_code)
        {
        case 200:
            return "OK";
        case 404:
            return "Not Found";
        case 301:
            return "Moved Permanently";
        case 302:
            return "Found";
        default:
            return std::string();
        }
    }

    // 后缀转标准
    std::string SuffixToDesc(const std::string &suffix)
    {
        if (suffix == ".html")
            return "text/html";
        else if (suffix == ".jpg")
            return "application/x-jpg";
        return "text/html";
    }

private:
    // 必备的要素
    std::string _verion;      // 浏览器版本
    int _status_code;         // 请求状态码 2XX:成功  4XX:网页找不到
    std::string _status_desc; // 状态码对应的描述
    std::string _content;
    std::unordered_map<std::string, std::string> _header_kv;

    // 最终要这4部分，构建应答
    std::string _resp_line;
    std::vector<std::string> _resp_header;
    std::string _blank_line;
    std::string _body;
};